
// The same as $.extend except this does not overwrite exiting attributes.
// Conceptually, this is more similar to multiple inheritance than classical interfaces.
// Instead of enforcing that the class provide a set of methods explicitly,
// implmemented interfaces just provide defaults values for said methods.
$.implmement = function(){
    var k, i = 1, len = arguments.length, o = arguments[0];
    for(; i < len; i++) {
        for (k in arguments[i]) {
            if (undefined === o[k]) {
                o[k] = arguments[i][k];
            }
        }
    }
    return o;
};

// @param c - the object to use as the prototype
// @param e - optionally $.extend the object too.
// returns another object whose prototype is the provided (@param c) object.
$.beget = function(c,e){
    var F = function(){};
    F.prototype = c;
    var o = new F();
    if (e) $.extend(o,e);
    return o;
};

// the same as $.fn.css, except converts to and from pixel values on set and get 
$.fn.csspx = function(prop, val) {
    return val === undefined ? 
      parseInt(this.css(prop).replace('px',''),10) :
      this.css(prop,val + 'px');
};

// GEOMETRY

$.fn.bottom = function() {
    return $(this).offset().top + $(this).outerHeight();
}

$.fn.right = function() {
    return $(this).offset().left + $(this).outerWidth();
}


/* returns a rectangle representation of the bounds of an element */
$.fn.bounds = function(){
    
    var $this = $(this);
    var off = $this.offset();
    return {
        top: off.top,
        left: off.left,
        right: off.left + $this.width(),
        bottom: off.top + $this.height()
    };
    
};

// COLLISION DETECTION
// returns whether 2 elements overlap on the page.
// @param - other_el_ref - a jquery selector/reference to element to compare to
$.fn.overlaps = function(other_el_ref) {
    var tb = $(this).bounds(), ob = $(other_el_ref).bounds();

    return !(tb.right < ob.left || tb.left > ob.right
        || tb.bottom < ob.top || tb.top > ob.bottom);
};

// array indexof (not always implemented)
Array.prototype.indexOf=function(o,i){for(var j=this.length,i=i<0?i+j<0?0:i+j:i||0;i<j&&this[i]!==o;i++);return j<=i?-1:i}

if (typeof console === 'undefined') {
    console = {};
    console.log = console.warn = console.info = function(m){};
}
 
// sort child elements according to a comparison function, currently a case insensitive lexicographic inner text comparison.
$.fn.childSort = function(){
    return $(this).each(function() {
        var $select = $(this);
        var opts = $select.children('*').get();
        opts.sort(function(a, b) {
           var compA = $(a).text().toUpperCase();
           var compB = $(b).text().toUpperCase();
           return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
        });
        $select.empty();
        $.each(opts, function(idx, itm) { $select.append(itm); });
    });
};